/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.alphawavesgames.engine.rendering;

import com.alphawavesgames.engine.maths.Vector2;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

/**
 *
 * @author duncan
 * Provides interface to render sprites
 * Contains one dynamic vertex buffer that holds all the sprites added in one frame.
 * Therefore all sprites use the same material/shader/texture to render.
 * TODO: could add support for multiple materials.
 * TODO: add support for depth sorting
 */
public class SpriteRenderer
{
    private RenderHWInterface m_renderInterface;

    //private List< Sprite > m_renderList;
    private boolean m_inScene;

    private static final int MAX_BATCH_QUADS = 512;
    private static final int MAX_BATCH_VERTS = MAX_BATCH_QUADS * 4;
    private static final int MAX_BATCHES = 64;
    //dynamic vertex buffer to store all the verts used to draw all the batches
    protected RenderHWInterface.VertexBuffer m_batchBuffer;
    //index buffer to stitch all the quads into batches
    //this will contain static data as the indices never change
    protected RenderHWInterface.IndexBuffer m_indexBuffer;
    //TODO : create a class to hold the data for each batch and put the texture handle/ref in there
    //        so we can bind it in the draw fn
    //markers to denote the batch segments
    protected int[] m_batchEnds;
    protected TextureManager.TextureHandle[] m_batchTextures;
    //current position in the batch vertex buffer;
    protected int m_currVBPos;
    //current batch index
    protected int m_currBatch;

    public SpriteRenderer()
    {
        m_renderInterface = null;
        m_inScene = false;
        //m_renderList = new ArrayList< Sprite >();
        m_batchBuffer = null;
        m_indexBuffer = null;
        m_currBatch = 0;
        m_currVBPos = 0;

        m_batchEnds = new int[ MAX_BATCHES ];
        m_batchTextures = new TextureManager.TextureHandle[ MAX_BATCHES ];
    }

    private void resetBatches()
    {
        m_currBatch = 0;
        m_currVBPos = 0;
        for( int i = 0; i < MAX_BATCHES; i++ )
        {
            m_batchEnds[ i ] = 0;
        }
    }

    public void initialise( RenderContainer renderContainer )
    {
        m_renderInterface = renderContainer.getRenderHWInterface();
        m_batchBuffer = VertexPos3Tex2.createVBForVertexType( m_renderInterface, MAX_BATCH_VERTS );
        m_indexBuffer = m_renderInterface.createIndexBuffer();

        int numIndices = MAX_BATCH_QUADS * 6;
        char[] indexData = new char[ numIndices ];

        char quadVert = 0;
        for( int i = 0; i < numIndices; i += 6, quadVert += 4 )
        {
            indexData[ i + 0 ] = ( char ) (quadVert + 0);
            indexData[ i + 1 ] = ( char ) (quadVert + 1);
            indexData[ i + 2 ] = ( char ) (quadVert + 2);
            indexData[ i + 3 ] = ( char ) (quadVert + 1);
            indexData[ i + 4 ] = ( char ) (quadVert + 3);
            indexData[ i + 5 ] = ( char ) (quadVert + 2);
        }

        m_indexBuffer.create( numIndices, indexData );
    }

    public void finalise()
    {

    }


    public void beginScene()
    {
        assert( m_inScene == false );
        //clear the batch list for the new frame
        resetBatches();
        m_inScene = true;
    }

    public void endScene()
    {
        assert( m_inScene == true );
        int lastBatch = m_currBatch == 0 ? 0 : m_currBatch - 1;
        if( m_currVBPos > m_batchEnds[ lastBatch ] )
        {
            //mark the final batch to end at the current vb pos
            m_batchEnds[ m_currBatch ] = m_currVBPos;
            m_currBatch++;
        }
        m_inScene = false;
    }

    //render all of the queued sprites
    public void render()
    {
        //shouldn't be calling this until scene has finished
        assert( m_inScene == false );

        /*ListIterator<Sprite> spriteIterator = m_renderList.listIterator();

        while( spriteIterator.hasNext() )
        {
            Sprite currSprite = spriteIterator.next();
            currSprite.draw();
        }*/

        //bind the vertex buffer

        int startIndex = 0;
        //go through all the batches and render
        for( int i = 0; i < m_currBatch; i++ )
        {
            drawBatch( startIndex, m_batchEnds[ i ] );
            startIndex = m_batchEnds[ i ] + 1;
        }
    }

    private void drawBatch( int startIndex, int endIndex )
    {
        //todo - render the quads from start to end
        //renderVB( startIndex, endIndex )

        //set up states
        //bind the vertex buffer, index buffer and texture
        m_batchBuffer.bind();
        m_indexBuffer.bind();
        //now draw the quads
        int numTris = ( endIndex - startIndex ) / 2;
        m_renderInterface.drawIndexedTriList( numTris, startIndex);
    }

    public Sprite createSprite( TextureManager.TextureHandle texture, int x, int y, int width, int height )
    {
        return new Sprite( texture, x, y, width, height );
    }

    public Sprite createSprite( TextureManager.TextureHandle texture )
    {
        return new Sprite( texture );
    }

    

    public class Sprite
    {
        //the four vertices that make the quad for the sprite
        private Vertex2DCol[] m_quadVerts;
        //position of the centre of the sprite
        private Vector2 m_centrePos;
        //half extents of the sprite - vector from centre to top-left
        private Vector2 m_halfExtents;
        //handle to the texture for the sprite
        protected TextureManager.TextureHandle m_texture;

        protected int m_xPos;
        protected int m_yPos;
        protected int m_width;
        protected int m_height;

        protected Sprite()
        {
            m_quadVerts = new Vertex2DCol[4];
            m_centrePos = new Vector2(0.0f);
            m_halfExtents = new Vector2(0.0f);
            m_texture = null;

            m_xPos = m_yPos = m_width = m_height = 0;
        }

        protected Sprite( TextureManager.TextureHandle texHandle )
        {
            m_quadVerts = new Vertex2DCol[4];
            m_centrePos = new Vector2(0.0f);
            m_halfExtents = new Vector2(0.0f);
            m_texture = texHandle;

            m_xPos = m_yPos = m_width = m_height = 0;
        }

        protected Sprite( TextureManager.TextureHandle texture, int x, int y, int width, int height )
        {
            m_quadVerts = new Vertex2DCol[4];
            m_centrePos = new Vector2(0.0f);
            m_halfExtents = new Vector2(0.0f);
            m_texture = texture;

            m_xPos = x;
            m_yPos = y;
            m_width = width;
            m_height = height;
        }

        //lazy...using integer pos/extents to make it easy
        public void setPosAndExtents( int x, int y, int width, int height )
        {
            m_xPos = x;
            m_yPos = y;
            m_width = width;
            m_height = height;
        }

        public void setAlpha( int alpha )
        {
            //todo : feed it through to the quad verts
        }

        public void setTexture( TextureManager.TextureHandle texHandle )
        {
            m_texture = texHandle;
        }

        //submit the sprite to be drawn during rendering
        public void submitDraw()
        {
            assert( m_inScene == true );
            //m_renderList.add( this );
        }

        protected void draw()
        {
            //draw the sprite
        }

    }

}
